package graphics.ParticleSystem;


import graphics.GraphicalObject;

import java.nio.FloatBuffer;
import java.util.Random;

import javax.media.opengl.GL;
import javax.naming.PartialResultException;

import com.sun.opengl.util.BufferUtil;

public class AbstractParticleSystem extends GraphicalObject{

	int max_points = 400;
	int cur_points = 0;
	int new_points = 5;
	int size = 3;
	
	
	float decay_rate = 0.05f;
	
	FloatBuffer points;
	FloatBuffer colors;
	float[] points_arr;
	float[] colors_arr;
	float[] change_arr;
	float[] life_arr;
	
	Random random = new Random();
	boolean isInitializedParticles = false;
	@Override
	public void initialize(GL gl) {
		super.initialize(gl);

		initializeParticles();
	}

	private void initializeParticles() {
		isInitializedParticles = true;
		points = BufferUtil.newFloatBuffer(max_points*3);
		colors = BufferUtil.newFloatBuffer(max_points*3);
	
		points_arr = new float[max_points*3];
		colors_arr = new float[max_points*3];
		change_arr = new float[max_points*3];
		life_arr = new float[max_points];
		
		for (int i = 0; i < max_points; i++){
			life_arr[i] = 0;
		}
	}

	@Override
	public void render(GL gl) {
		if (!isInitializedParticles)
			initializeParticles();
		
		super.render(gl);
		
		cleardead();
		addpoints();
		updatepoints();

		doRender(gl);
	}

	private void doRender(GL gl) {
		gl.glDisable(GL.GL_LIGHTING);
		
		points.put( points_arr, 0, cur_points * 3 );
		points.rewind();
	    colors.put( colors_arr, 0, cur_points * 3 );
	    colors.rewind();

	    gl.glEnableClientState( GL.GL_VERTEX_ARRAY );
		gl.glEnableClientState( GL.GL_COLOR_ARRAY );
	    gl.glVertexPointer( 3, GL.GL_FLOAT, 0, points );
		gl.glColorPointer( 3, GL.GL_FLOAT, 0, colors );
	    gl.glPointSize(size);
		gl.glDrawArrays( GL.GL_POINTS, 0, cur_points);

		gl.glEnable(GL.GL_LIGHTING);
	}

	private void cleardead(){
		int last_index = max_points - 1;
		int n = cur_points;
		for (int i = 0; i < n; i++){
			
			if (i >= last_index) break;
			if (life_arr[i] > 0) continue;
			
			
			// find alive to switch with dead
			while (life_arr[last_index] <= 0 &&  last_index > i){
				last_index--;
			}
			cur_points--;
			if (i >= last_index) break;
			switchPoints(i,last_index);
			
		}
	}
	private void addpoints() {
		int limit = Math.min(cur_points + new_points, max_points);

		for (int i = cur_points; i < limit; i++){
			float[] args = createPoint();
			points_arr[i*3+0] = args[0];
			points_arr[i*3+1] = args[1];
			points_arr[i*3+2] = args[2];


			change_arr[i*3+0] = args[3];
			change_arr[i*3+1] = args[4];
			change_arr[i*3+2] = args[5];

			colors_arr[i*3+0] = args[6];
			colors_arr[i*3+1] = args[7];
			colors_arr[i*3+2] = args[8];
			life_arr[i] = args[9];
		}
		cur_points = limit;
	}
	private void updatepoints(){
		
		for (int i = 0; i < cur_points; i++){
			updatePosition(i);
			updateColor(i);
			updateLife(i);
		}
	}


	private void switchPoints(int i, int last) {
		points_arr[i*3+0] = points_arr[last*3+0];
		points_arr[i*3+1] = points_arr[last*3+1];
		points_arr[i*3+2] = points_arr[last*3+2];
		colors_arr[i*3+0] = colors_arr[last*3+0];
		colors_arr[i*3+1] = colors_arr[last*3+1];
		colors_arr[i*3+2] = colors_arr[last*3+2];
		change_arr[i*3+0] = change_arr[last*3+0];
		change_arr[i*3+1] = change_arr[last*3+1];
		change_arr[i*3+2] = change_arr[last*3+2];
		life_arr[i] = life_arr[last];
		life_arr[last] = 0;
	}

	protected float[] createPoint(){
		return new float[] {
				random.nextFloat(),random.nextFloat(),random.nextFloat(),
				0,0.5f,0,
				random.nextFloat(),random.nextFloat(),random.nextFloat(),
				1};
	}
	protected void updatePosition(int i) {
		points_arr[i*3+0] += change_arr[i*3+0];
		points_arr[i*3+1] += change_arr[i*3+1];
		points_arr[i*3+2] += change_arr[i*3+2];
	}
	protected void updateColor(int i) {
	}
	protected void updateLife(int i) {
		life_arr[i] -= decay_rate;
	}

	public int getMax_points() {
		return max_points;
	}

	public void setMax_points(int maxPoints) {
		max_points = maxPoints;
	}

	public int getNew_points() {
		return new_points;
	}

	public void setNew_points(int newPoints) {
		new_points = newPoints;
	}

	public float getDecay_rate() {
		return decay_rate;
	}

	public void setDecay_rate(float decayRate) {
		decay_rate = decayRate;
	}
	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	@Override
	public void setParameter(String name, String params[]){
		if (name.equals("decay")) {
			setDecay_rate(Float.parseFloat(params[0]));
		}
		if (name.equals("max")) {
			setMax_points(Integer.parseInt(params[0]));
		}
		if (name.equals("rate")) {
			setNew_points(Integer.parseInt(params[0]));
		}
		if (name.equals("size")) {
			setSize(Integer.parseInt(params[0]));
		}
		super.setParameter(name, params);
	}
	

}